iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Mobile Development

我將成為Swift之強者系列 第 18

Day18 - 仿刻 iOS 鬧鐘實作:新增與編輯鬧鐘(上)

  • 分享至 

  • xImage
  •  

Day18 - 仿刻 iOS 鬧鐘實作:新增與編輯鬧鐘(上)

昨天我們已經完成了 MainTableViewCell 的設計,並能夠在主畫面上正確顯示鬧鐘的時間與狀態。
接下來,我們要進入新增與編輯鬧鐘的部分,也就是 AddAlarmViewController 的功能。

今天的內容會專注在上半段程式:畫面初始化、UI 設定、鬧鐘的新增、更新與刪除


1. 導入套件與協議設計

import UIKit
import RealmSwift

protocol AddAlarmViewControllerDelegate: AnyObject {
    func didAddNewAlarm()
    func didUpdateAlarm()
    func didDeleteAlarm()
}

這裡匯入 UIKit 與 RealmSwift,並建立一個協議,讓新增、更新、刪除鬧鐘後能通知主畫面刷新。


2. 主要屬性設定

class AddAlarmViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet weak var DatePicker: UIDatePicker!
    @IBOutlet weak var tbvAddAlarm: UITableView!
    @IBOutlet weak var btnDelete: UIButton!
    
    weak var delegate: AddAlarmViewControllerDelegate?
    var repeatDays: [Bool] = Array(repeating: false, count: 7)
    var selectedSound: String = ""
    var alarmname: String = ""
    var alarmToEdit: AlarmData?
    var snoozeEnabled: Bool = true

這些屬性會對應到 UI 與鬧鐘資料:

  • repeatDays 用來記錄一週七天是否要重複。
  • alarmToEdit 判斷目前是新增還是編輯模式。
  • selectedSound、alarmname、snoozeEnabled 則對應提示聲、標籤名稱與稍後提醒設定。

3. ViewDidLoad 與畫面初始化

override func viewDidLoad() {
    super.viewDidLoad()
    tbvAddAlarm.delegate = self
    tbvAddAlarm.dataSource = self
    tbvAddAlarm.register(UINib(nibName: "AddAlarmTableViewCell", bundle: nil), forCellReuseIdentifier: "AddAlarmTableViewCell")
    setUI()
    setupTextFieldDelegate()
    
    if let alarm = alarmToEdit, !alarm.isInvalidated {
        repeatDays = Array(alarm.repeatDays)
        selectedSound = alarm.sound
        alarmname = alarm.name
        snoozeEnabled = alarm.snoozeEnabled
        if let date = formatStringToDate(alarm.alarmTime) {
            DatePicker.date = date
        }
    }
}

這裡的程式邏輯包含三個部分:

  1. 設定 TableView 的委派與資料來源。
  2. 註冊自訂的 Cell。
  3. 如果是編輯模式,會把原本的鬧鐘資料回填到畫面上。

4. UI 設定與操作按鈕

func setUI() {
    DatePicker.locale = Locale(identifier: "zh_TW")
    title = alarmToEdit == nil ? "加入鬧鐘" : "編輯鬧鐘"
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "儲存", style: .plain, target: self, action: #selector(doneTapped))
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "取消", style: .plain, target: self, action: #selector(cancelTapped))
    
    btnDelete.isHidden = alarmToEdit == nil
    btnDelete.setTitle("刪除鬧鐘", for: .normal)
    btnDelete.setTitleColor(.red, for: .normal)
    btnDelete.addTarget(self, action: #selector(deleteTapped), for: .touchUpInside)
}

在新增模式下,刪除按鈕會被隱藏;
在編輯模式下,則會顯示刪除功能。


5. 儲存、更新與刪除鬧鐘

儲存新鬧鐘

func saveNewAlarm() {
    let realm = try! Realm()
    let newAlarm = AlarmData(
        alarmTime: formatDate(DatePicker.date),
        creatTime: getSystemTime(),
        name: alarmname,
        repeatDays: repeatDays,
        sound: selectedSound,
        snoozeEnabled: snoozeEnabled
    )
    try! realm.write { realm.add(newAlarm) }
    scheduleNotification(for: newAlarm)
    delegate?.didAddNewAlarm()
    self.dismiss(animated: true, completion: nil)
}

更新已存在鬧鐘

func updateAlarm(_ alarm: AlarmData) {
    let realm = try! Realm()
    try! realm.write {
        alarm.alarmTime = formatDate(DatePicker.date)
        alarm.name = alarmname.isEmpty ? "鬧鐘" : alarmname
        alarm.repeatDays.removeAll()
        alarm.repeatDays.append(objectsIn: repeatDays)
        alarm.sound = selectedSound
        alarm.snoozeEnabled = snoozeEnabled
    }
    scheduleNotification(for: alarm)
    delegate?.didUpdateAlarm()
    self.dismiss(animated: true, completion: nil)
}

刪除鬧鐘

@objc func deleteTapped() {
    guard let alarm = alarmToEdit else { return }
    let realm = try! Realm()
    try! realm.write { realm.delete(alarm) }
    delegate?.didDeleteAlarm()
    self.dismiss(animated: true, completion: nil)
}

這三個方法分別對應到新增、更新、刪除鬧鐘的資料操作,並且會通知主畫面更新。


小結

今天我們完成了 AddAlarmViewController 的上半部分,包含 UI 初始化與資料操作。
到這裡為止,鬧鐘的新增、編輯與刪除功能已經具備。

明天的文章將會介紹 TableView 的顯示邏輯、頁面跳轉以及通知排程,讓這個頁面能完整運作。


上一篇
Day17 - 仿刻 iOS 鬧鐘實作:MainTableViewCell 介紹
下一篇
Day19 - 仿刻 iOS 鬧鐘實作:新增與編輯鬧鐘(下)
系列文
我將成為Swift之強者21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言